#include "DXUT.h"
#include "RenderHeightMap.h"
#include "..\\UIControlsDef.h"
#define  NDF_DIM  512
#define  SCALEFACTOR 20

CRenderHeightMap::CRenderHeightMap(void)
{
	m_pXYZFile = NULL;
	m_bDataReady = false;

	m_pWorldViewProjection = NULL;
	m_pRenderHeightMapTech = NULL;
	m_pRenderHeightMapNormalTech = NULL;
	m_pVertexLayout = NULL;
	m_pEffect = NULL;
	m_pVB = NULL;
	m_pNormalLineVB = NULL;
	m_pNormalLineVB1 = NULL;
	m_pIB = NULL;

	m_pColorBar = NULL;

	D3DXMatrixIdentity(&m_matScaling);
}


CRenderHeightMap::~CRenderHeightMap(void)
{
	SAFE_DELETE(m_pXYZFile);
	SAFE_DELETE(m_pColorBar);
}

int CRenderHeightMap::LoadXYZFile(char* pXYZFileName, float fWidth, float fHeight)
{
	if(pXYZFileName!=NULL)
	{
		if(m_pXYZFile == NULL)
			m_pXYZFile = new CXYZFileIO();
		else
			m_pXYZFile->reset();

		if(m_pXYZFile->LoadFromFile(pXYZFileName, fWidth, fHeight)<0)
			return -1;
		else
		{
			//Recreate VB and IB
			//create vertex buffer
			ID3D11Device* pd3dDevice = DXUTGetD3D11Device();
			HRESULT hr = CD3DUtilityFunction::CreateVBVec3(pd3dDevice, m_pXYZFile->m_iHMDataWidth, m_pXYZFile->m_iHMDataHeight, m_pXYZFile->m_fSampleWidth, 
				m_pXYZFile->m_fSampleHeight, -0.5f, -0.5f, m_pXYZFile->m_arrHeightMapData, &m_pVB);
			//create index buffer
			hr = CD3DUtilityFunction::CreateIBGrid32Bit(pd3dDevice, m_pXYZFile->m_iHMDataWidth, m_pXYZFile->m_iHMDataHeight, &m_pIB);

			//create normal buffer
#ifdef USEPCANORMAL
			m_pXYZFile->EstimateNormalForEachPoint(3);
			hr = prepareNormalLineVB(pd3dDevice);
#endif
#ifdef USEAREAWEIGHTEDNORMAL
			m_pXYZFile->EstimateNormalAreaWeighted_IgnoreNoDataPt();
			hr = prepareNormalLineVB1(pd3dDevice);
#endif
			m_bDataReady = true;
			//prepare scaling matrix
			float iDim = m_pXYZFile->m_iHMDataWidth > m_pXYZFile->m_iHMDataHeight ? m_pXYZFile->m_iHMDataWidth:m_pXYZFile->m_iHMDataHeight;
			D3DXMatrixScaling(&m_matScaling,SCALEFACTOR/iDim,SCALEFACTOR/iDim,SCALEFACTOR/iDim);

			return 0;
		}


	}
	return -1;
}

int CRenderHeightMap::LoadXYZFile(CDirectoryUtil* pDirUtil, enum SUBDIRECTORY iSubFolder, TCHAR* pFileName, float fWidth, float fHeight)
{
	TCHAR DataFileDir[MAX_PATH];
	pDirUtil->GetAbsoluteFileFolder(pFileName,iSubFolder,DataFileDir);
	char dataFileDir[MAX_PATH];
	size_t  iSize;
	// Conversion
    wcstombs_s(&iSize, dataFileDir, (size_t)MAX_PATH, 
               DataFileDir, (size_t)MAX_PATH);
	return LoadXYZFile(dataFileDir, fWidth,fHeight);

}

void CRenderHeightMap::InitEffectsVariables()
{
	m_pRenderHeightMapTech = m_pEffect->GetTechniqueByName( "RenderHeightMap" );
	m_pRenderHeightMapNormalTech = m_pEffect->GetTechniqueByName("RenderHeightMapNormal");
	m_pWorldViewProjection = m_pEffect->GetVariableByName( "mWorldViewProjection" )->AsMatrix();
	m_pHeightRange = m_pEffect->GetVariableByName("mHeightRange")->AsScalar();
	m_pNormalDebugColor = m_pEffect->GetVariableByName("vNormalDebugColor")->AsVector();
	m_pColorBarTex = m_pEffect->GetVariableByName("texColorBar")->AsShaderResource();
}

HRESULT CRenderHeightMap::OnD3D11CreateDevice(ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, CDirectoryUtil* pDirUtil, void* pUserContext)
{
	if(m_pXYZFile==NULL)	
		printf("No .xyz file loaded!\n");
	
	HRESULT hr = S_OK;
	//create effects
	TCHAR ShaderFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderHeightMap.fx",subfolder, ShaderFileDir);
	hr = CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect);	
	InitEffectsVariables();
	//create vertex layout
	const D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };
	hr = CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,layout, ARRAYSIZE(layout), m_pRenderHeightMapTech,&m_pVertexLayout);   
	DXUT_SetDebugName( m_pVertexLayout, "VBLayout" );
	
	if(hr != S_OK)
		m_bDataReady = false;

	//assist
	SAFE_DELETE(m_pColorBar);
	m_pColorBar = new CRenderColorBar(256);
	hr = m_pColorBar->OnD3D11CreateDevice(pd3dDevice,pBackBufferSurfaceDesc,pDirUtil,pUserContext);

	return hr;
}

void CRenderHeightMap::ReloadEffectsDebug(ID3D11Device* pd3dDevice, CDirectoryUtil* pDirUtil)
{
	HRESULT hr = S_OK;
	SAFE_RELEASE(m_pEffect);
	//create effects
	TCHAR ShaderFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderHeightMap.fx",subfolder, ShaderFileDir);
	hr = CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect);	
	InitEffectsVariables();

	m_pColorBar->ReloadEffectsDebug(pd3dDevice,pDirUtil);
}

HRESULT CRenderHeightMap::OnD3D11ResizedSwapChain(ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                         const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, CDirectoryUtil* pDirUtil, void* pUserContext)
{
	HRESULT hr = S_OK;
	hr = m_pColorBar->OnD3D11ResizedSwapChain(pd3dDevice,pSwapChain,pBackBufferSurfaceDesc,pDirUtil,pUserContext);
	return hr;
}

void CRenderHeightMap::OnD3D11ReleasingSwapChain(void* pUserContext)
{
	m_pColorBar->OnD3D11ReleasingSwapChain(pUserContext);
}
void CRenderHeightMap::OnD3D11DestroyDevice( void* pUserContext )
{
	SAFE_RELEASE(m_pEffect);
	SAFE_RELEASE(m_pVB);
	SAFE_RELEASE(m_pNormalLineVB);
	SAFE_RELEASE(m_pNormalLineVB1);
	SAFE_RELEASE(m_pIB);
	SAFE_RELEASE(m_pVertexLayout);

	m_pColorBar->OnD3D11DestroyDevice(pUserContext);

	//remove UIs
	RemoveGUIComp();
}

void CRenderHeightMap::OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, CBaseCamera* pViewCamera, 
	CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, void* pUserContext )
{
	HRESULT hr = S_OK;
	if(!m_bDataReady)
	{
		printf("Data is NOT ready!\n");
		return;
	}

    // Get the projection & view matrix from the camera class
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
    D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
	mWorld = mWorld*m_matScaling;
	D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
    D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
    D3DXMATRIX mWorldViewProjection = mWorld * mView * mProj;
	
	V(m_pWorldViewProjection->SetMatrix((float*)(&mWorldViewProjection)));
	V(m_pHeightRange->SetFloat(m_pXYZFile->m_fHeightRange));
	V(m_pColorBarTex->SetResource(m_pColorBar->m_pColorBarTexSR));

	// Set render resources
    pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = { m_pVB };
    UINT pStride[1] = { sizeof(D3DXVECTOR3) };
    UINT pOffset[1] = { 0 };
    pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetIndexBuffer(m_pIB, DXGI_FORMAT_R32_UINT, 0);
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	m_pRenderHeightMapTech->GetDesc( &Desc );

	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		m_pRenderHeightMapTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->DrawIndexed( (m_pXYZFile->m_iHMDataWidth-1)*(m_pXYZFile->m_iHMDataHeight-1)*6, 0, 0 );
		//pd3dImmediateContext->DrawIndexed( 6, 0, 0 );
	}

	//render normal
#ifdef USEPCANORMAL
	RenderNormalLines(pd3dDevice,pd3dImmediateContext,pViewCamera,pDirUtil,fTime,fElapsedTime,pUserContext);	
#endif
	
#ifdef USEAREAWEIGHTEDNORMAL
	//RenderNormalLines1(pd3dDevice,pd3dImmediateContext,pViewCamera,pDirUtil,fTime,fElapsedTime,pUserContext);	
#endif

	//render color bar
	m_pColorBar->OnD3D11FrameRender(pd3dDevice,pd3dImmediateContext,pViewCamera,pDirUtil,fTime,fElapsedTime,pUserContext);
}



HRESULT CRenderHeightMap::prepareNormalLineVB(ID3D11Device* pd3dDevice)
{
	HRESULT hr = S_OK;
	//release old buffer
	SAFE_RELEASE(m_pNormalLineVB);

	unsigned int iNumElem = m_pXYZFile->m_arrVertices.size();

	if(iNumElem == 0)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
		return E_FAIL;
	}

	D3DXVECTOR3* pVBData = new D3DXVECTOR3[iNumElem*2];
	float fScale = 0.50f;
	for(int i = 0; i < iNumElem; i++)
	{
		*(pVBData+2*i) = m_pXYZFile->m_arrVertices[i].Position;
		*(pVBData+2*i+1) = m_pXYZFile->m_arrVertices[i].Normal*fScale + *(pVBData+2*i);
		//printf("Normal line: (%f,%f,%f) to (%f,%f,%f)\n",(pVBData+2*i)->x, (pVBData+2*i)->y, (pVBData+2*i)->z, (pVBData+2*i+1)->x, (pVBData+2*i+1)->y, (pVBData+2*i+1)->z);
	}

	V(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,sizeof(D3DXVECTOR3),(float*)(pVBData),iNumElem*2,&m_pNormalLineVB));

	SAFE_DELETE_ARRAY(pVBData);	
	return hr;
}

HRESULT CRenderHeightMap::prepareNormalLineVB1(ID3D11Device* pd3dDevice)
{
	HRESULT hr = S_OK;
	//release old buffer
	SAFE_RELEASE(m_pNormalLineVB1);

	unsigned int iNumElem = m_pXYZFile->m_arrNormalAreaWeighted.size();

	if(iNumElem == 0)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
			return E_FAIL;
	}

	D3DXVECTOR3* pVBData = new D3DXVECTOR3[iNumElem*2];
	float fScale = 0.5f;
	for(int i = 0; i < iNumElem; i++)
	{
		*(pVBData+2*i) = m_pXYZFile->m_arrNormalAreaWeighted[i].Position;
		*(pVBData+2*i+1) = m_pXYZFile->m_arrNormalAreaWeighted[i].Normal*fScale + *(pVBData+2*i);		
	}

	V(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,sizeof(D3DXVECTOR3),(float*)(pVBData),iNumElem*2,&m_pNormalLineVB1));

	SAFE_DELETE_ARRAY(pVBData);	
	return hr;
}

void CRenderHeightMap::RenderNormalLines(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, 
	CBaseCamera* pViewCamera, CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, void* pUserContext)
{
	HRESULT hr = S_OK;
	if(!m_bDataReady)
	{
		printf("Data is NOT ready!\n");
		return;
	}

	// Get the projection & view matrix from the camera class
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
	D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
	mWorld = mWorld*m_matScaling;
	D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
	D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
	D3DXMATRIX mWorldViewProjection = mWorld * mView * mProj;
	hr = m_pWorldViewProjection->SetMatrix((float*)(&mWorldViewProjection));

	D3DXVECTOR4 color = D3DXVECTOR4(1,1,0,1);
	m_pNormalDebugColor->SetFloatVector((float*)&color);
	// Set render resources
	pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = { m_pNormalLineVB };
	UINT pStride[1] = { sizeof(D3DXVECTOR3) };
	UINT pOffset[1] = { 0 };
	pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINELIST);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	m_pRenderHeightMapNormalTech->GetDesc( &Desc );

	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		m_pRenderHeightMapNormalTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->Draw(m_pXYZFile->m_arrVertices.size()*2,0);		
	}
}

void CRenderHeightMap::RenderNormalLines1(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, 
	CBaseCamera* pViewCamera, CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, void* pUserContext)
{
	HRESULT hr = S_OK;
	if(!m_bDataReady)
	{
		printf("Data is NOT ready!\n");
		return;
	}

	// Get the projection & view matrix from the camera class
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
	D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
	mWorld = mWorld*m_matScaling;
	D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
	D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
	D3DXMATRIX mWorldViewProjection = mWorld * mView * mProj;
	hr = m_pWorldViewProjection->SetMatrix((float*)(&mWorldViewProjection));

	D3DXVECTOR4 color = D3DXVECTOR4(1,1,0,1);
	m_pNormalDebugColor->SetFloatVector((float*)&color);
	// Set render resources
	pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = { m_pNormalLineVB1 };
	UINT pStride[1] = { sizeof(D3DXVECTOR3) };
	UINT pOffset[1] = { 0 };
	pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINELIST);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	m_pRenderHeightMapNormalTech->GetDesc( &Desc );

	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		m_pRenderHeightMapNormalTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->Draw(m_pXYZFile->m_arrNormalAreaWeighted.size()*2,0);		
	}
}

void CRenderHeightMap::AddGUIComp(CDXUTDialog* pControlHud, CDXUTDialog* pControlSampleUI, int iX, int& iY, int iYo, int iWidth, int iHeight)
{
	if(pControlSampleUI != NULL)
		m_pCurControlDlg = pControlSampleUI;
	else if(pControlHud != NULL)
		m_pCurControlDlg = pControlHud;
	
	//int iYtmp = iY;
	//m_pCurControlDlg->AddButton(IDC_BUTTON_LOADXYZFILE,L"Load .xyz",0,iYtmp+=iYo,iWidth,iHeight);	
}

void CRenderHeightMap::OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
}

void CRenderHeightMap::RemoveGUIComp()
{
	if(!m_pCurControlDlg)
		return;
	m_pCurControlDlg->RemoveControl(IDC_BUTTON_LOADXYZFILE);
}